iT邦幫忙

0

深入淺出設計守則2

  • 分享至 

  • xImage
  •  

深入淺出設計守則2

小明收到需求, 需求如下:

麥當雞系統需要統計消費次數的報表, 報表內容需要列出每一種消費種類的消費次數.

麥當雞系統中有一個消費種類Enum 叫做ChargeType

public enum ChargeType 
{
   DuckNuggets,
   ChickenNuggets,
   FrenchFries,
   FriedChickenChops,
   GreenBeanSoup
}

小明寫了統計方法

public ChargeStatistics Compute(ChargeType[] chargeHistory)
{
   var data = new ChargeStatistics();
   foreach (var r in chargeHistory) 
   {
      if (r == ChargeType.DuckNuggets)
         data.DuckNuggets++;
      else if (r == ChargeType.ChickenNuggets)
         data.ChickenNuggets++;
      else if (r == ChargeType.FrenchFries)
         data.FrenchFries++;
      else if (r == ChargeType.FriedChickenChops)
         data.FriedChickenChops++;
      else if (r == ChargeType.GreenBeanSoup)
         data.GreenBeanSoup++;
   }
   return data;
}

可以改進的地方

觀察上述的程式碼, 可以發現到

  • 有重複的程式碼
  • 如果要新增ChargeType 的種類, 要修改統計的程式碼

設計守則 如果你有大量的資料型別的資料, 就考慮可能將資料組織起來,形成一個物件類.
集合資料的方法有很多種, 資料結構可以用陣列也可以利用集合(List/Dictionary/...).

故統計的結果物件可以修改為

public class ChargeStatistics
{
   public Dictionary<ChargeType, int> Data { get; set; }
}

然後在統計方法內改寫為

public ChargeStatistics Compute(ChargeType[] chargeHistory)
{
   var result = new ChargeStatistics();
   foreach (var r in chargeHistory) 
   {
      IncreaseCounter(result.Data, r);
   }
   return result;
}

private void IncreaseCounter(Dictionary<ChargeType, int> data, ChargeType chargeType)
{
   if (!data.TryGetValue(r, out var counter))
   {
      data[r] = 1;
   }
   else
   {
      data[r] = counter + 1;
   }
}

我們可以直接使用Dictionary 字典的特性, 來統計計算每一種消費種類的次數.

在IncreaseCounter 方法中, 檢查data 是否出現了某個ChargeType ,

  • 如果有, 則對應的ChargeType 計數自增
  • 如果沒有, 則對應的ChargeType 計數 = 1

LINQ 查詢語法 (LINQ Query Syntax)

在整理資料的時候常常都需要給資料做分組, 以便更進一步的分析及處理,
而C# 有提供分組統計的方法, 我們可以拿來利用.

我們可以利用LINQ Group 語法將chargeHistory 資料做分組, 然後再用Count() 方法統計次數
故Compute() 方法可以改寫為

public ChargeStatistics Compute(ChargeType[] chargeHistory)
{
   var result = new ChargeStatistics();
   var q1 = from tb1 in chargeHistory
         group tb1 by tb1 into g1
         select new
         {
            Key = g1.Key,
            Count = g1.Count()
         };
   result.Data = q1.ToDictionary(x => x.Key, x => x.Count);
   return result;
}

LINQ 方法語法 (LINQ Method Syntax)

當然假如你不喜歡這種長得有點像SQL 語法的運算式, 你也能改寫為Linq Method

public ChargeStatistics Compute(ChargeType[] chargeHistory)
{
   var result = new ChargeStatistics();
   var q1 = chargeHistory.GroupBy(x => x)
         .Select(new 
         {
            Key = g1.Key,
            Count = g1.Count()
         });
   result.Data = q1.ToDictionary(x => x.Key, x => x.Count);
   return result;
}

讓程式碼會說話

最後在這個 "統計結果物件" 這個地方, Data 屬性的宣告有點模糊,

public class ChargeStatistics
{
   public Dictionary<ChargeType, int> Data { get; set; }
}

對於不熟悉麥當雞系統的維護人員可能會不知道這個 Dictionary<ChargeType, int> 裡面的 int 代表的是甚麼意思.

故我們可以新增一個物件來明確宣告 "一個 ChargeType 的統計資料"

public class ChargeTypeStatistic
{
   public ChargeType ChargeType { get; set; }
   public int Count { get; set; }
}

然後宣告Data 屬性為List ,
這樣就可以明確讓維護人員知道這個Data 的定義

public class ChargeStatistics
{
   public List<ChargeTypeStatistic> Data { get; set; }
}

當然統計方法也要修改為下面方式

public ChargeStatistics Compute(ChargeType[] chargeHistory)
{
   var result = new ChargeStatistics();
   result.Data = chargeHistory.GroupBy(x => x)
         .Select(new ChargeTypeStatistic
         {
            ChargeType = g1.Key,
            Count = g1.Count()
         })
         .List();
   return result;
}

如此一來維護人員也比較清楚知道統計結果物件裡面的代表意義,
對程式碼的理解將不必再依靠繁雜的註解和厚厚的文件.


圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言